package com.zhongyouyun.other.intercepter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.zhongyouyun.other.annotation.DataAuthScope;
import com.zhongyouyun.other.aspect.DataAuthScopeAspect;
import com.zhongyouyun.other.aspect.XianMuAuthAspesct;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Properties;

/**
 * mybatis 拦截顺序Executor -> StatementHandler->ParameterHandler->ResultSetHandler
 * 要在分页插件之前完成sql语句的修改 应拦截Executor
 *
 * @author
 */
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class DataAuthInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        //由于逻辑关系，只会进入一次
        if (args.length == 4) {
            //4 个参数时
            //---------------这里就是拷贝的CachingExecutor或者BaseExecutor的代码
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            //6 个参数时
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }
        //-------------本来一般情况下是执行invocation.proceed(),继续执行拦截方法，但这里直接执行这个方法，相
        //-------------当于替换了CachingExecutor或者BaseExecutor原来的实现。
        String originalSql = boundSql.getSql();
        originalSql = this.modifyOrgSql(originalSql);
        BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), originalSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
        //解决mybatis分页foreach 参数失效问题：There is no getter for property named ‘__frch_ _0’ in 'class
        //得知新版需要用到additionalParameters参数，也就是说不论哪个版本，value都是从metaParameters获得，
        //在老版本中使用metaParameters新版中使用了additionalParameters判断是否有值。而分页插件没有注入该参数，用ref加入该参数(注释的代码)，问题解决
        /* if (ReflectUtil.getFieldValue(boundSql,"additionalParameters") != null){
            Object metaParameters = ReflectUtil.getFieldValue(boundSql, "additionalParameters");
            ReflectUtil.setFieldValue(newBoundSql,"additionalParameters",metaParameters);
        }*/
        // todo ReflectUtil方法在有物理分页的情况下foreach #{item}可以正常取值，但是在没有物理分页的情况下foreach #{item}取值为null,故修改为以下方式
        for (ParameterMapping mapping : boundSql.getParameterMappings()) {
            String prop = mapping.getProperty();
            if (boundSql.hasAdditionalParameter(prop)) {
                newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
            }
        }
        return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, newBoundSql);
    }

    /**
     * 根据权限点拼装对应sql
     *
     * @return 拼装后的sql
     */
    private String modifyOrgSql(String originalSql) {
        Object dataAuthSql = DataAuthScopeAspect.DATA_AUTH_THREAD_LOCAL.get();
        Object xianguuTypeAuthSql = XianMuAuthAspesct.XIANG_MU_TYPE_LOCAL.get();
//        System.out.println(dataAuthSql);
        if (ObjectUtil.isNotEmpty(dataAuthSql) || ObjectUtil.isNotEmpty(xianguuTypeAuthSql)) {
            StringBuffer b = new StringBuffer("select * from (" + originalSql + ") temp_data_scope where 1=1 ");
            if (ObjectUtil.isNotEmpty(dataAuthSql)) {
                b.append(" " + dataAuthSql.toString());
            }
            if (ObjectUtil.isNotEmpty(xianguuTypeAuthSql)) {
                b.append(" " + xianguuTypeAuthSql);
            }
            System.out.println(b.toString());
            return b.toString();
        } else {
            return originalSql;
        }
    }
}